using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using TripleA.Core.Interfaces.Entities;

namespace TripleA.Core.Extensions.Process
{
    public class RunProcessStep<T> : StepBase<T>
        where T: Context
    {
        public string ExeFilePath { get; set; }
        public string Args { get; set; }

        /// <summary>
        /// The number of milliseconds to wait for the process to exit. Execution suspends until timeout expires
        /// and then throws an <see cref="ApplicationException"/>. Set this to <see cref="int.MaxValue"/> to wait 
        /// for ever.
        /// </summary>
        public int? WaitForExit { get; set; }

        /// <summary>
        /// The expected exit code of the process. Only checked if set in conjunction with <see cref="WaitForExit"/>
        /// </summary>
        public int? ExitCode { get; set; }

        public override void Execute(T context)
        {
            var name = Path.GetFileName(ExeFilePath);
            var psi = new ProcessStartInfo
                          {
                              Arguments = Args,
                              FileName = ExeFilePath,
                              RedirectStandardOutput = ExitCode.HasValue,
                              UseShellExecute = false
                          };

            var proc = System.Diagnostics.Process.Start(psi);
            
            if (WaitForExit.HasValue)
            {
                var exitMsg = proc.StandardOutput.ReadToEnd();

                if (!proc.HasExited)
                {                  
                    if (!proc.WaitForExit(WaitForExit.Value))
                    {
                        throw new ApplicationException(string.Format("Process '{0}' has still not terminated after {1}ms",
                                                                     name, WaitForExit.Value));
                    }
                }

                if (ExitCode.HasValue && proc.ExitCode != ExitCode.Value)
                {
                    var sb = new StringBuilder();
                    sb.AppendFormat("Process {1} exited with code {2}, expected {3}{0}", Environment.NewLine, name, proc.ExitCode, ExitCode.Value);
                    sb.AppendFormat("Output from {1} =====>{0}", Environment.NewLine, name);
                    sb.AppendLine(exitMsg);

                    throw new ApplicationException(sb.ToString());
                }
            }
        }
    }
}